/**
 * Copyright (c) 2010
 * Bert Young. UESTC. 
 */

#ifndef BERT_BUFFER_H
#define BERT_BUFFER_H

#include <vector>
#include <cstring>
#include <cassert>
#include "Allocator.h"

const unsigned int _64KB = 64 * 1024;

#define TRUNK_NUM(size) (((size)+_64KB-1) & ~( _64KB - 1 ))

#define ALIGN 8
#define ADJUST_SIZE(size) ( ((size)+ALIGN-1) & ~(ALIGN-1))

template < typename BUFFER >
class Buffer
{
	/**
	 * The max capacity of buffer
	 */
	unsigned int maxSize;
	/**
	 * The starting address can be read
	 */
	unsigned int rPtr;
	/**
	 * The starting address can be write
	 */
	unsigned int wPtr;
	/**
	 * The real internal buffer
	 */
	BUFFER buffer;
public:
	/**
	 * Should be specialized
	 */
	Buffer();
	/**
	 * Put data into internal buffer
	 */
	void put(const unsigned char * buf, unsigned int size)
	{
		assureSpace(size);
		bcopy(buf, writeAddr(), size);
		adjustWritePtr(size);
	}
	/**
	 * Return the starting address can be read
	 */
	unsigned char * readAddr()
	{
		return &buffer[rPtr];
	}
	/**
	 * Return the starting address can be write
	 */
	unsigned char * writeAddr()
	{
		return &buffer[wPtr];
	}
	/**
	 * Adjust the starting address can be write
	 */
	void adjustWritePtr(unsigned int size)
	{
		wPtr += size;
	}
	/**
	 * Adjust the starting address can be read
	 */
	void adjustReadPtr(unsigned int size)
	{
		rPtr += size;
		/*if ( sizeForRead() > 0 )
		{
			if ( sizeForRead() <= rPtr )
			{
				memmove(&buffer[0], readAddr(), sizeForRead());
				rPtr = 0;
				wPtr = sizeForRead();
			}
		}
		else
			reset();*/
		if ( sizeForRead() == 0 )
			reset();
		else if ( sizeForRead() <= rPtr )
		{
			memmove(&buffer[0], readAddr(), sizeForRead());
			rPtr = 0;
			wPtr = sizeForRead();
		}
	}
	/**
	 * Return the size can be read
	 */
	unsigned int sizeForRead() const
	{
		if ( wPtr > rPtr )
			return wPtr - rPtr;
		else
			return 0;
	}
	/**
	 * Return the size can be write
	 */
	unsigned int sizeForWrite() const
	{
		return maxSize - wPtr;
	}
	/**
	 * To assure sufficient memory
	 */
	void assureSpace(unsigned int size);
	/**
	 * Reuse buffer
	 */
	void reset()
	{
		rPtr = wPtr = 0;
	}
	/**
	 * Max capacity
	 */
	unsigned int capacity() const
	{
		return maxSize;
	}
};

/**
 * TO DO : Consider for memory pool
 */
//typedef Buffer< std::vector<unsigned char> > FlexibleBuffer;
typedef Buffer< Bert::vector<unsigned char> > FlexibleBuffer;

/**
 * Resize vector to assure sufficient memory
 */
template <>
inline void FlexibleBuffer::assureSpace(unsigned int size)
{
	size = ADJUST_SIZE(size);
	if ( sizeForWrite() < size )
	{
		maxSize = TRUNK_NUM(maxSize + size) * _64KB;
		buffer.resize(maxSize);
	}
}

/**
 * Array buffer
 */
typedef Buffer< unsigned char [_64KB-1] > StackBuffer;
/**
 * Array on the stack can not resize
 */
template <>
inline void StackBuffer::assureSpace(unsigned int size)
{
	assert( sizeForWrite() >= size );
}

#endif

